package com.eobard.obfuscate.core;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eobard.adapter.BaseVisitorAdapter;
import com.eobard.config.obfuscate.ObfuscatorConfig;
import com.eobard.obfuscate.IObfuscator;
import com.eobard.params.RenameMethodParams;
import com.eobard.utils.CollectionUtils;
import com.eobard.utils.ExceptionUtils;
import com.eobard.utils.ObfuscatorUtils;
import com.eobard.utils.RandomExpression;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: divenswu(核心代码)、Eobard Gu(重构代码)
 * @Date: 2025-05-19
 * @Description: 重命名方法名的核心混淆类
 */
@Component
public class RenameMethodObfuscator extends BaseVisitorAdapter<RenameMethodParams> implements IObfuscator {
    @Value("${obfuscate.enable.typeSolver.logging:false}")
    private boolean print;
    private String sourceRoot;
    private final Map<String, CompilationUnit> fileMap = new HashMap<>();
    private final List<Set<String>> ancestorMethodList = new ArrayList<>();
    private final Map<String, List<MethodCallExpr>> methodCallExprMap = new ConcurrentHashMap<>();
    private final Map<String, MethodDeclaration> normalMethodDeclarationMap = new ConcurrentHashMap<>();
    private final Map<String, MethodDeclaration> overrideMethodDeclarationMap = new ConcurrentHashMap<>();

    //初始化数据
    @Override
    public void initialize(ObfuscatorConfig config) {
        ExceptionUtils.checkNonNull(config.getSourceRoot(), "请先设置项目路径!");
        //清空历史记录
        CollectionUtils.clear(ancestorMethodList);
        CollectionUtils.clear(methodCallExprMap, normalMethodDeclarationMap, overrideMethodDeclarationMap);
        this.sourceRoot = config.getSourceRoot();
        //添加代码引用
        initSolver(sourceRoot, config.getSourceExt());
        //初始化当前的源码空间:获取源文件代码
        ObfuscatorUtils.loopFiles(fileMap, sourceRoot);
    }


    //是否启用这个功能
    @Override
    public boolean supports(ObfuscatorConfig config) {
        return ObjectUtil.isNotEmpty(config.getRenameMethodParams());
    }

    /**
     * 遍历出哪些函数可以进行参数修改
     **/
    @Override
    public void visit(ClassOrInterfaceDeclaration cid, RenameMethodParams renameMethodParams) {
        super.visit(cid, renameMethodParams);
        //这里是获取全部的普通成员函数,然后放入normalMethodDeclarationMap集合中
        if (!cid.isInnerClass() && !cid.isInterface()) {
            cid.getParentNode().ifPresent(parentNode -> {
                if (parentNode instanceof CompilationUnit) {
                    CompilationUnit cu = (CompilationUnit) parentNode;
                    cu.getPackageDeclaration().ifPresent(packageDeclaration -> {
                        String packageName = packageDeclaration.getNameAsString();
                        if (renameMethodParams.getPackageList().isEmpty() || renameMethodParams.getPackageList().stream().anyMatch(packageName::startsWith)) {
                            for (MethodDeclaration md : cid.getMethods()) {
                                if (
                                        md.getAnnotationByName("Override").isEmpty() &&
                                                !md.isNative() && !md.isAbstract()
                                ) {
                                    try {
                                        //第一版判断
                                        String methodSignature = md.resolve().getQualifiedSignature();
                                        normalMethodDeclarationMap.put(methodSignature, md);
                                        //==============================⬇️================================//
                                        //新版判断:只将属于指定包下的普通方法放入集合中
//                                        ResolvedMethodDeclaration rmd = md.resolve();
//                                        String methodPkg = rmd.declaringType().getPackageName();
//                                        if (renameMethodParams.getPackageList().stream().anyMatch(methodPkg::startsWith)) {
//                                            normalMethodDeclarationMap.put(rmd.getQualifiedSignature(), md);
//                                        }
                                        //==============================⬆️================================//
                                    } catch (Exception ignore) {
                                    }
                                }
                            }
                        }
                    });
                }
            });
        }
        //这里是获取全部的继承的函数
        for (MethodDeclaration md : cid.getMethods()) {
            try {
                //第一版判断
                String methodSignature = md.resolve().getQualifiedSignature();
                overrideMethodDeclarationMap.put(methodSignature, md);
                //==============================⬇️================================//
                //第二版判断:只将属于指定包下的继承方法放入集合中
//                ResolvedMethodDeclaration rmd = md.resolve();
//                String packageName = rmd.declaringType().getPackageName();
//                if (renameMethodParams.getPackageList().stream().anyMatch(packageName::startsWith)) {
//                    overrideMethodDeclarationMap.put(rmd.getQualifiedSignature(), md);
//                }
                //==============================⬆️================================//
            } catch (Exception ignore) {
            }
        }
        if (!cid.isInnerClass() && !cid.isInterface()) {
            //获取父类函数中的所有函数
            Map<String, List<String>> ams = new LinkedHashMap<>();
            try {
                for (ResolvedReferenceType ancestor : cid.resolve().getAncestors()) {
                    for (ResolvedMethodDeclaration x : ancestor.getAllMethods()) {
                        //第一版判断
                        if (x.getQualifiedSignature().startsWith(renameMethodParams.getBasePackage())) {
                            if (!x.getName().equalsIgnoreCase(RandomExpression.getLoggerPrintName())) {
                                ams.computeIfAbsent(x.getSignature(), k -> new ArrayList<>()).add(x.getQualifiedSignature());
                            }
                        }
                        //==============================⬇️================================//
                        //第二版判断:只将属于指定包下的父类方法放入集合中
//                        String packageName = x.declaringType().getPackageName();
//                        if (renameMethodParams.getPackageList().stream().anyMatch(packageName::startsWith)) {
//                            if (x.getQualifiedSignature().startsWith(renameMethodParams.getBasePackage())) {
//                                if (!x.getName().equalsIgnoreCase(RandomExpression.getLoggerPrintName())) {
//                                    ams.computeIfAbsent(x.getSignature(), k -> new ArrayList<>()).add(x.getQualifiedSignature());
//                                }
//                            }
//                        }
                        //==============================⬆️================================//
                    }
                }
                //获取当前类中的函数
                for (MethodDeclaration md : cid.getMethods()) {
                    for (String signature : ams.keySet()) {
                        if (signature.equals(md.resolve().getSignature())) {
                            if (!md.getNameAsString().equalsIgnoreCase(RandomExpression.getLoggerPrintName())) {
                                ams.get(signature).add(0, md.resolve().getQualifiedSignature());
                            }
                        }
                    }
                }
            } catch (Exception ignored) {
            }
            //聚合所有相同实现的类到同一个地方
            for (List<String> values : ams.values()) {
                if (values.size() <= 1) {
                    continue;
                }
                boolean has = false;
                for (Set<String> ancestorMethods : ancestorMethodList) {
                    for (String value : values) {
                        if (ancestorMethods.contains(value)) {
                            has = true;
                            ancestorMethods.addAll(values);
                        }
                    }
                }
                if (!has) {
                    ancestorMethodList.add(new HashSet<>(values));
                }
            }
        }
    }


    /**
     * 这里处理函数调用时，对参数的修改
     **/
    @Override
    public void visit(MethodCallExpr mc, RenameMethodParams renameMethodParams) {
        super.visit(mc, renameMethodParams);
        mc.toMethodCallExpr().ifPresent(resolvedMethod -> {
            try {
                //第一版判断
                String methodSignature = resolvedMethod.resolve().getQualifiedSignature();
                methodCallExprMap.computeIfAbsent(methodSignature, k -> new ArrayList<>()).add(mc);
                //==============================⬇️================================//
                //第二版判断:限定调用方只统计属于指定包下的方法
//                ResolvedMethodDeclaration rmd = resolvedMethod.resolve();
//                String packageName = rmd.declaringType().getPackageName();
//                if (renameMethodParams.getPackageList().stream().anyMatch(packageName::startsWith)) {
//                    methodCallExprMap.computeIfAbsent(rmd.getQualifiedSignature(), k -> new ArrayList<>()).add(mc);
//                }
                //==============================⬆️================================//
            } catch (Exception ignored) {
                if (print) {
                    System.err.println(ignored.getMessage());
                }
            }
        });
    }


    //混淆核心代码
    @Override
    public void obfuscate(ObfuscatorConfig config) {
        RenameMethodParams renameMethodParams = config.getRenameMethodParams();
        ExceptionUtils.checkNonNull(renameMethodParams.getBasePackage(), "请先配置【重命名方法名混淆器】的basePackage!");
        ExceptionUtils.checkNonNull(renameMethodParams.getPackageList(), "请先配置【重命名方法名混淆器】的包列表!");

        //计算当前需要修改的函数方法
        this.fileMap.forEach((path, cu) -> cu.accept(this, renameMethodParams));
        //修改两种共有的函数
        Collection<String> methodSignatures = normalMethodDeclarationMap.keySet();
        if (renameMethodParams.isReferenceMethodOnly()) {
            methodSignatures = CollectionUtil.intersection(normalMethodDeclarationMap.keySet(), methodCallExprMap.keySet());
        }
        //进行普通的函数进行rename
        for (String methodSignature : methodSignatures) {
            //1.修改函数名称
            MethodDeclaration methodDeclaration = normalMethodDeclarationMap.get(methodSignature);
            String newName = RandomExpression.getName();
            methodDeclaration.setName(newName);
            //2.修改引用点函数名称
            List<MethodCallExpr> methodCallExprList = methodCallExprMap.get(methodSignature);
            if (null != methodCallExprList) {
                for (MethodCallExpr mc : methodCallExprList) {
                    mc.setName(newName);
                }
            }
        }
        //进行继承的函数进行rename
        for (Set<String> ancestorMethods : ancestorMethodList) {
            //修改名字
            String newName = RandomExpression.getName();
            for (String ancestorMethod : ancestorMethods) {
                //1.修改函数名称
                MethodDeclaration methodDeclaration = overrideMethodDeclarationMap.get(ancestorMethod);
                methodDeclaration.setName(newName);
                //4.3.对引用点添加参数信息
                List<MethodCallExpr> methodCallExprList = methodCallExprMap.get(ancestorMethod);
                if (null != methodCallExprList) {
                    for (MethodCallExpr mc : methodCallExprList) {
                        mc.setName(newName);
                    }
                }
            }
        }
        ObfuscatorUtils.saveObfuscatedFiles(fileMap);
    }

}
